﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using TianShu.Data;

namespace TianShu.WebApi
{
    public class ExtraPropertyDictionaryConverter : JsonConverterFactory
    {
        public override bool CanConvert(Type typeToConvert)
        {

            if (typeToConvert != typeof(ExtraPropertyDictionary))
            {
                return false;
            }

            return true;
        }

        public override JsonConverter CreateConverter(
            Type type,
            JsonSerializerOptions options)
        {
            Type keyType = typeof(string);
            Type valueType = typeof(object);

            JsonConverter converter = (JsonConverter)Activator.CreateInstance(
                typeof(ExtraPropertyDictionaryConverterInner),
                BindingFlags.Instance | BindingFlags.Public,
                binder: null,
                args: new object[] { options },
                culture: null);

            return converter;
        }

        private class ExtraPropertyDictionaryConverterInner : JsonConverter<ExtraPropertyDictionary>
        {
            private readonly JsonConverter<object> _valueConverter;

            public ExtraPropertyDictionaryConverterInner(JsonSerializerOptions options)
            {
                // For performance, use the existing converter if available.
                _valueConverter = (JsonConverter<object>)options
                    .GetConverter(typeof(object));

            }

            public override ExtraPropertyDictionary Read(
                ref Utf8JsonReader reader,
                Type typeToConvert,
                JsonSerializerOptions options)
            {
                if (reader.TokenType != JsonTokenType.StartObject)
                {
                    throw new JsonException();
                }

                var dictionary = new ExtraPropertyDictionary();

                while (reader.Read())
                {
                    if (reader.TokenType == JsonTokenType.EndObject)
                    {
                        return dictionary;
                    }

                    // Get the key.
                    if (reader.TokenType != JsonTokenType.PropertyName)
                    {
                        throw new JsonException();
                    }

                    string propertyName = reader.GetString();

                    if (propertyName.IsNullOrEmpty())
                    {
                        throw new JsonException(
                            $"Unable to convert \"{propertyName}\" to string.");
                    }
                    string key = propertyName.FistCharToUpper();
                    object value;
                    if (_valueConverter != null)
                    {
                        reader.Read();
                        value = _valueConverter.Read(ref reader, typeof(object), options);
                    }
                    else
                    {
                        value = JsonSerializer.Deserialize<object>(ref reader, options);
                    }

                    // Add to dictionary.
                    dictionary.Add(key, value);
                }

                throw new JsonException();
            }

            public override void Write(
                Utf8JsonWriter writer,
                ExtraPropertyDictionary dictionary,
                JsonSerializerOptions options)
            {
                writer.WriteStartObject();

                foreach ((var key, var value) in dictionary)
                {
                    var propertyName = key.FistCharToUpper();
                    writer.WritePropertyName
                        (options.PropertyNamingPolicy?.ConvertName(propertyName) ?? propertyName);

                    JsonSerializer.Serialize(writer, value, options);

                }

                writer.WriteEndObject();
            }
        }
    }
}
